package com.mc.fastkit.view.shape

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.RectF
import android.util.AttributeSet
import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatImageView
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.CornerTreatment
import com.google.android.material.shape.CutCornerTreatment
import com.google.android.material.shape.EdgeTreatment
import com.google.android.material.shape.RoundedCornerTreatment
import com.google.android.material.shape.ShapeAppearanceModel
import com.google.android.material.shape.ShapeAppearancePathProvider
import com.mc.fastkit.R
import com.mc.fastkit.ext.getCornerSize
import kotlin.math.min

/**
 * ShapedImageView
 * @author: MasterChan
 * @date: 2023-05-04 09:55:41
 */
@SuppressLint("RestrictedApi")
open class ShapedImageView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {

    private val shapeModelBuilder = ShapeAppearanceModel.builder()
    private var shapeModel: ShapeAppearanceModel
    private val pathProvider = ShapeAppearancePathProvider.getInstance()
    private var maskPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var strokePaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val maskPath = Path()
    private var maskRect = RectF()
    private val strokePath = Path()
    private var destination = RectF()
    private var dashEffect: DashPathEffect? = null

    var isRounded = false
        private set
    var strokeWidth = 0f
        private set
    var strokeColor = 0
        private set
    var dashWidth = 0f
        private set
    var dashGap = 0f
        private set
    val topLeftCornerSize: Float
        get() = shapeModel.topLeftCornerSize.getCornerSize(RectF())
    val topRightCornerSize: Float
        get() = shapeModel.topRightCornerSize.getCornerSize(RectF())
    val bottomLeftCornerSize: Float
        get() = shapeModel.bottomLeftCornerSize.getCornerSize(RectF())
    val bottomRightCornerSize: Float
        get() = shapeModel.bottomRightCornerSize.getCornerSize(RectF())
    val topLeftCorner: CornerTreatment
        get() = shapeModel.topLeftCorner
    val topRightCorner: CornerTreatment
        get() = shapeModel.topRightCorner
    val bottomLeftCorner: CornerTreatment
        get() = shapeModel.bottomLeftCorner
    val bottomRightCorner: CornerTreatment
        get() = shapeModel.bottomRightCorner
    val leftEdge: EdgeTreatment
        get() = shapeModel.leftEdge
    val topEdge: EdgeTreatment
        get() = shapeModel.topEdge
    val rightEdge: EdgeTreatment
        get() = shapeModel.rightEdge
    val bottomEdge: EdgeTreatment
        get() = shapeModel.bottomEdge

    init {
        maskPaint.color = Color.WHITE
        maskPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
        strokePaint.style = Paint.Style.STROKE

        val a = context.obtainStyledAttributes(attrs, R.styleable.ShapedImageView)
        isRounded = a.getBoolean(R.styleable.ShapedImageView_mc_isRounded, false)
        strokeWidth = a.getDimension(R.styleable.ShapedImageView_mc_strokeWidth, 0f)
        strokeColor = a.getColor(R.styleable.ShapedImageView_mc_strokeColor, 0)
        dashWidth = a.getDimension(R.styleable.ShapedImageView_mc_dashWidth, 0f)
        dashGap = a.getDimension(R.styleable.ShapedImageView_mc_dashGap, 0f)
        val cornerFamily = a.getInteger(R.styleable.ShapedImageView_mc_cornerFamily, 0)
        val cornerFamilyTl = a.getInteger(
            R.styleable.ShapedImageView_mc_cornerFamilyTopLeft, cornerFamily
        )
        val cornerFamilyTr = a.getInteger(
            R.styleable.ShapedImageView_mc_cornerFamilyTopRight, cornerFamily
        )
        val cornerFamilyBl = a.getInteger(
            R.styleable.ShapedImageView_mc_cornerFamilyBottomLeft, cornerFamily
        )
        val cornerFamilyBr = a.getInteger(
            R.styleable.ShapedImageView_mc_cornerFamilyBottomRight, cornerFamily
        )
        val cornerSize = a.getCornerSize(R.styleable.ShapedImageView_mc_cornerSize) { 0f }
        val cornerSizeTl = a.getCornerSize(
            R.styleable.ShapedImageView_mc_cornerSizeTopLeft, cornerSize
        )
        val cornerSizeTr = a.getCornerSize(
            R.styleable.ShapedImageView_mc_cornerSizeTopRight, cornerSize
        )
        val cornerSizeBl = a.getCornerSize(
            R.styleable.ShapedImageView_mc_cornerSizeBottomLeft, cornerSize
        )
        val cornerSizeBr = a.getCornerSize(
            R.styleable.ShapedImageView_mc_cornerSizeBottomRight, cornerSize
        )
        a.recycle()

        shapeModel = shapeModelBuilder.setTopLeftCorner(cornerFamilyTl, cornerSizeTl)
            .setTopRightCorner(cornerFamilyTr, cornerSizeTr)
            .setBottomLeftCorner(cornerFamilyBl, cornerSizeBl)
            .setBottomRightCorner(cornerFamilyBr, cornerSizeBr)
            .build()
        dashEffect = DashPathEffect(floatArrayOf(dashWidth, dashGap), 0f)
    }

    override fun onDetachedFromWindow() {
        setLayerType(LAYER_TYPE_NONE, null)
        super.onDetachedFromWindow()
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        setLayerType(LAYER_TYPE_HARDWARE, null)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        updateShapeMask(width, height)
    }

    fun setRounded(isRounded: Boolean) = apply {
        this.isRounded = isRounded
    }

    fun setStrokeWidth(strokeWidth: Float) = apply {
        this.strokeWidth = strokeWidth
    }

    fun setStrokeColor(@ColorInt color: Int) = apply {
        strokeColor = color
    }

    fun setStrokeColor(color: String) = apply {
        setStrokeColor(Color.parseColor(color))
    }

    fun setDash(dashWidth: Float, dashGap: Float) = apply {
        this.dashWidth = dashWidth
        this.dashGap = dashGap
        this.dashEffect = DashPathEffect(floatArrayOf(dashWidth, dashGap), 0f)
    }

    fun setCornerSize(cornerSize: Float) = apply {
        shapeModelBuilder.setAllCornerSizes(cornerSize)
    }

    fun setCornerSizeTopLeft(cornerSize: Float) = apply {
        shapeModelBuilder.setTopLeftCornerSize(cornerSize)
    }

    fun setCornerSizeTopRight(cornerSize: Float) = apply {
        shapeModelBuilder.setTopRightCornerSize(cornerSize)
    }

    fun setCornerSizeBottomLeft(cornerSize: Float) = apply {
        shapeModelBuilder.setBottomLeftCornerSize(cornerSize)
    }

    fun setCornerSizeBottomRight(cornerSize: Float) = apply {
        shapeModelBuilder.setBottomRightCornerSize(cornerSize)
    }

    fun setCornerFamily(@CornerFamily cornerFamily: Int) = apply {
        shapeModelBuilder.setAllCorners(
            if (cornerFamily == CornerFamily.CUT) CutCornerTreatment() else RoundedCornerTreatment()
        )
    }

    fun setTopLeftCornerTreatment(cornerTreatment: CornerTreatment) = apply {
        shapeModelBuilder.setTopLeftCorner(cornerTreatment)
    }

    fun setTopRightCornerTreatment(cornerTreatment: CornerTreatment) = apply {
        shapeModelBuilder.setTopRightCorner(cornerTreatment)
    }

    fun setBottomLeftCornerTreatment(cornerTreatment: CornerTreatment) = apply {
        shapeModelBuilder.setBottomLeftCorner(cornerTreatment)
    }

    fun setBottomRightCornerTreatment(cornerTreatment: CornerTreatment) = apply {
        shapeModelBuilder.setBottomRightCorner(cornerTreatment)
    }

    fun setCorner(@CornerFamily cornerFamily: Int, cornerSize: Float) = apply {
        shapeModelBuilder.setAllCorners(cornerFamily, cornerSize)
    }

    fun setTopLeftCorner(@CornerFamily cornerFamily: Int, cornerSize: Float) = apply {
        shapeModelBuilder.setTopLeftCorner(cornerFamily, cornerSize)
    }

    fun setTopRightCorner(@CornerFamily cornerFamily: Int, cornerSize: Float) = apply {
        shapeModelBuilder.setTopRightCorner(cornerFamily, cornerSize)
    }

    fun setBottomLeftCorner(@CornerFamily cornerFamily: Int, cornerSize: Float) = apply {
        shapeModelBuilder.setBottomLeftCorner(cornerFamily, cornerSize)
    }

    fun setBottomRightCorner(@CornerFamily cornerFamily: Int, cornerSize: Float) = apply {
        shapeModelBuilder.setBottomRightCorner(cornerFamily, cornerSize)
    }

    fun setEdge(edgeTreatment: EdgeTreatment) = apply {
        shapeModelBuilder.setAllEdges(edgeTreatment)
    }

    fun setLeftEdge(edgeTreatment: EdgeTreatment) = apply {
        shapeModelBuilder.setLeftEdge(edgeTreatment)
    }

    fun setTopEdge(edgeTreatment: EdgeTreatment) = apply {
        shapeModelBuilder.setTopEdge(edgeTreatment)
    }

    fun setRightEdge(edgeTreatment: EdgeTreatment) = apply {
        shapeModelBuilder.setRightEdge(edgeTreatment)
    }

    fun setBottomEdge(edgeTreatment: EdgeTreatment) = apply {
        shapeModelBuilder.setBottomEdge(edgeTreatment)
    }

    fun setup() {
        shapeModel = shapeModelBuilder.build()
        updateShapeMask(width, height)
        invalidate()
        invalidateOutline()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawPath(maskPath, maskPaint)
        drawStroke(canvas)
    }

    private fun updateShapeMask(width: Int, height: Int) {
        var left = 0f
        var top = 0f
        var right = width.toFloat()
        var bottom = height.toFloat()
        if (isRounded) {
            val diameter = min(width, height)
            left = (width - diameter) / 2f
            top = (height - diameter) / 2f
            right = left + diameter
            bottom = top + diameter
            shapeModel = shapeModel.toBuilder()
                .setAllCorners(CornerFamily.ROUNDED, (diameter - strokeWidth) / 2f)
                .build()
        }
        val rect = RectF(left, top, right, bottom)
        rect.inset(strokeWidth / 2f, strokeWidth / 2f)
        destination.set(rect)
        pathProvider.calculatePath(shapeModel, 1f, destination, strokePath)
        maskPath.rewind()
        maskPath.addPath(strokePath)
        maskRect.set(0f, 0f, width.toFloat(), height.toFloat())
        maskPath.addRect(maskRect, Path.Direction.CCW)

        setPaddingRelative(
            super.getPaddingStart(), super.getPaddingTop(), super.getPaddingEnd(),
            super.getPaddingBottom()
        )
    }

    private fun drawStroke(canvas: Canvas) {
        strokePaint.strokeWidth = strokeWidth
        if (strokeWidth > 0) {
            strokePaint.color = strokeColor
            strokePaint.pathEffect = dashEffect
            canvas.drawPath(strokePath, strokePaint)
        }
    }
}